<template>
  <div class="auth-menu-container">
    <div class="tree-select-header">
      <ASpaceCompact>
        <AButton :icon="createVNode(SwitcherOutlined)" :disabled="!treeData?.length" @click="onExpandedAll">
          展开/折叠
        </AButton>
        <AButton
          :type="isCheckedAll && treeData?.length !== 0 ? 'primary' : 'default'"
          :disabled="!treeData?.length"
          @click="onCheckedAll"
        >
          全选/全不选
        </AButton>
        <AButton
          :type="linkage ? 'primary' : 'default'"
          :disabled="!treeData?.length"
          @click="() => setLinkage(!linkage)"
        >
          父子联动
        </AButton>
      </ASpaceCompact>

      <ASpace>
        <AButton
          type="primary"
          :icon="createVNode(SaveOutlined)"
          :loading="loading"
          :disabled="initialKeys === selectKeys?.join(',')"
          @click="onSubmit"
        >
          保存
        </AButton>
      </ASpace>
    </div>

    <RTreeSelect
      :loading="spinning"
      :treeData="treeData"
      :selectKeys="selectKeys"
      v-model:expandedKeys="expandedKeys"
      @change="onCheckedChange"
    >
    </RTreeSelect>
  </div>
</template>

<script setup>
import { createVNode, onMounted, computed, toValue, watch } from 'vue';
import { RTreeSelect } from '@/components/custom';
import { SaveOutlined, SwitcherOutlined } from '@ant-design/icons-vue';
import { useState, useFetch } from '@/hooks';
import { arrayDeepFlat, arrayDiff, arrayCross, arrayFilter } from '@/utils/array';
import { fetchGetAllocatedMenuTree, fetchUpdateAllocatedMenu } from '@/api/system/roleAuth';

defineOptions({
  inheritAttrs: false
});

const props = defineProps({
  open: { type: Boolean, default: false },
  roleId: { type: [Number, String], default: () => null }
});

const [treeData, setTreeData] = useState([]);

const [initialKeys, setInitialKeys] = useState([]);
const [selectKeys, setSelectKeys] = useState([]);
const [expandedKeys, setExpandedKeys] = useState([]);

const [linkage, setLinkage] = useState(false);
const [deepData, setDeepData] = useState([]);
const selectAllKeys = computed(() => toValue(deepData)?.map(({ value }) => value));
const isCheckedAll = computed(() => toValue(deepData)?.length === toValue(selectKeys)?.length);

const { spinning, onFetch: onFetchTreeData } = useFetch(fetchGetAllocatedMenuTree, false);
const { spinning: loading, onFetch: onFetchUpdate } = useFetch(fetchUpdateAllocatedMenu, false);

const onLoadData = async () => {
  const { menuTree, menuIds = [] } = await onFetchTreeData(props?.roleId);
  setTreeData(menuTree);
  setSelectKeys(menuIds);
  setInitialKeys(menuIds?.join(','));
  setDeepData(arrayDeepFlat(menuTree));
};

onMounted(() => {
  onLoadData();
});

watch(
  () => props.open,
  (open) => {
    if (open) {
      onLoadData();
    } else {
      setTreeData([]);
      setExpandedKeys([]);
      setSelectKeys([]);
      setInitialKeys([]);
      setLinkage(false);
    }
  }
);

const onSubmit = async () => {
  await onFetchUpdate({ roleId: props?.roleId, menuIds: toValue(selectKeys) });
  setInitialKeys(toValue(selectKeys)?.join(','));
};

const onCheckedAll = () => {
  const status = toValue(selectKeys)?.length === toValue(selectAllKeys).length;
  setSelectKeys(status ? [] : toValue(selectAllKeys));
};

const onExpandedAll = () => {
  const status = toValue(expandedKeys)?.length === toValue(selectAllKeys).length;
  setExpandedKeys(status ? [] : toValue(selectAllKeys));
};

const onCheckedChange = (checked) => {
  if (toValue(linkage)) {
    // 获取当前选择值
    const cur = arrayDiff(checked, toValue(selectKeys)).pop();
    checked = handleParentLinkage(cur, checked);
    checked = handleChildrenLinkageChange(cur, checked);
    setSelectKeys(checked);
  } else {
    setSelectKeys(checked);
  }
};

const handleParentLinkage = (cur, checked) => {
  // 获取当前节点 [父级联动]
  const parent = toValue(deepData)?.find(({ children }) => children?.find(({ value }) => value === cur));

  const parentChildren = parent?.children.map(({ value }) => value);
  if (parentChildren && parentChildren.length) {
    const parentCross = arrayCross(checked, parentChildren); // 取交集
    if (parentChildren.length === parentCross.length) {
      checked.push(parent.value);
    } else {
      const index = checked.indexOf(parent.value);
      index !== -1 && checked.splice(index, 1);
    }
    checked = handleParentLinkage(parent.value, checked);
  }

  return checked;
};

const handleChildrenLinkageChange = (cur, checked) => {
  // 子节点联动修改
  const isChecked = checked.includes(cur);
  // 获取当前节点
  const curNode = toValue(deepData)?.find(({ value }) => value === cur);
  if (curNode.children) {
    // 获取当前所有子节点值
    const curChildKeys = arrayDeepFlat(curNode.children).map(({ value }) => value);
    if (isChecked) {
      checked.push(...curChildKeys);
      checked = [...new Set(checked)].sort();
    } else {
      // 过滤掉当前所有子节点
      checked = arrayFilter(checked, curChildKeys);
    }
  }

  return checked;
};
</script>

<style lang="scss" scoped>
.auth-menu-container {
  width: 100%;
  min-height: 60vh;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 16px;
  margin-bottom: 20vh;
  user-select: none;

  .tree-select-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px;
  }
}
</style>
